from loa_game import WHITE, BLACK, LinesOfActionState, SpinAction, MoveAction, DIRECTIONS, Direction
from game_agent import GameAgent
from alpha_beta import AlphaBetaSearch
from heu import alot, num_con, variance, glob_sum_dis, square
from time import time

# A bloody large number.
INFINITY = 1.0e400

class agent(GameAgent):
    
    #must be removed for testing only
    def __init__(self, depth=None):
        self.depth = depth
        
    def move(self, game_state):
        self.start = time()
        play = self.search(game_state)
        try:
            self.played.append(
                     game_state._getMoveAction( play.row, play.col, play.direction))
        except:
            self.played.append(
                     game_state._getSpinAction( play.row, play.col))
        return play
    
    def setup(self, player, game_state, turn_time_limit, setup_time_limit):
        self.player = player
        self.played = []
        self.move_time = turn_time_limit 
        u = lambda state: self.utility(state)
        #self.alphaBeta = AlphaBetaSearch(self.player, 3, u)
        self.max_depth = 3
        

    def utility(self, state):
        winner = state.getWinner()
        if winner is None:
            return alot(state)
        elif winner == self.player:
            return 100
        else:
            return -100
    
    def time_left(self):
            return self.move_time-(time()-self.start)
    
    def search(self, current_state):
        
        best_value = -INFINITY
        first_time = None
        
        states = current_state.getSuccessors().items()
        states = map(lambda (x,y): (x,square(y),y),states)
        states.sort(lambda x,y: cmp(x[1],y[1]))
        states.reverse()
        
        for action,v,state in states:
            
            value_fn = self._getValueFn(state)
            if not first_time: start = time()
            value = value_fn(state, best_value, INFINITY, 1)
            if not first_time: 
                end = time()
                first_time = end - start
            
            if value > best_value:
                best_value = value
                best_action = action
            
            if self.time_left() < first_time:
                return best_action
            
        return best_action
    
    def _getValueFn(self, state):
        if state.getCurrentPlayer() == self.player:
            return self._maxValue
        else:
            return self._minValue
    
    def _cutoffTest(self, state, depth):
        
        return depth >= self.max_depth or (state.getWinner() is not None)
    
    def _maxValue(self, state, alpha, beta, depth):
        
        #player turn
        
        if self._cutoffTest(state, depth):
            return self.utility(state)
        
        successors = state.getSuccessors()
        successors = map(lambda x,y: (x,square(y),y), successors)
        successors.sort(lambda x,y:cmp(x[1],y[1]))
        successors.reverse()
        
        start = None
        value = -INFINITY
        for x,y,successor in successors:
            value_fn = self._getValueFn(successor)
            if not start: start = time()
            
            value = max(value, value_fn(successor, alpha, beta, depth + 1))
            if not start: 
                end = time()
                t = end - start
            if self.time_left() < t: return value
            
            if value >= beta:
                return value
            alpha = max(alpha, value)
        
        return value
    
    def _minValue(self, state, alpha, beta, depth):
        
        #other turn
        
        if self._cutoffTest(state, depth):
            return self.utility(state)
        
        successors = state.getSuccessors()
        
        successors = map(lambda x,y: (x,square(y),y), successors)
        successors.sort(lambda x,y:cmp(x[1],y[1]))
        start = None
        value = INFINITY
        for x,y,successor in successors:
            value_fn = self._getValueFn(successor)
            if not start: start = time()
            value = min(value, value_fn(successor, alpha, beta, depth + 1))
            if not start: 
                end = time()
                t = end-start
            if self.time_left() < t: return value
            if value <= alpha:
                return value
            beta = min(beta, value)
        
        return value

